/**
 * coloro.js - Lightweight JavaScript Color Picker
 * Copyright © Yoshiki Okawa http://www.yoshiki-okawa.com/
 * License: MIT License - http://www.opensource.org/licenses/mit-license.php
 *
 * Example: this.coloro = new Coloro();
 * Note: Must initialise using this.{name} or window.{name} because of a bug in IE:
 * http://social.msdn.microsoft.com/Forums/en/iewebdevelopment/thread/065a5b93-6474-4756-aaea-1fd55d4a013a
 * http://blogs.msdn.com/b/ericlippert/archive/2005/05/04/414684.aspx
 */
var doc = document;
if(typeof $ == "undefined"){
	function $(e){
		return doc.getElementById(e);
	}
}
//http://note19.com/2007/05/27/javascript-guid-generator/
function S4() {
	return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
}
function guid() {
	return (S4()+S4()+S4()+S4()+S4()+S4()+S4()+S4());
}
function getMouseXY(e) {
	if (e.pageX) {
		return {x:e.pageX,y:e.pageY};
	} else {
		var d = (doc.documentElement && doc.documentElement.scrollLeft) ? doc.documentElement : doc.body;
		return {x:e.clientX + d.scrollTop, y:e.clientY + d.scrollTop};
	}
}
//only for addEventListener
function bind(s, f) {
    return function () {
        f.apply(s, arguments);
    };
}
function addEL(o, t, f) {
	if (o.attachEvent)
		o.attachEvent("on"+t, f);
	else
		o.addEventListener(t, f, false);
}
function delEL(o, t, f) {
	if (o.detachEvent)
		o.detachEvent("on"+t, f);
	else
		o.removeEventListener(t, f, false);
}

function px2int(px){
	return parseInt(px.split(/px/i).join(""));
}

function startDragParent(e, target){
	if(!e) e=window.event;
	var mouseXY = getMouseXY(e);
	var info = {state:1, distX:mouseXY.x-px2int(target.style.left), distY:mouseXY.y-px2int(target.style.top)};
	//I hate this but looks like the only solution here...
	var moveElement = function(e){
		if(!e) e=window.event;
		var mouseXY = getMouseXY(e);
		target.style.left = (mouseXY.x-info.distX)+"px";
		target.style.top = (mouseXY.y-info.distY)+"px";
	}
	var stopElement = function(e){
		delEL(doc, "mousemove", moveElement);
		delEL(doc, "mouseup", stopElement);
		target.style["-moz-opacity"] = "1";
		target.style.opacity = "1";
	}
	addEL(doc, "mousemove", moveElement);
	addEL(doc, "mouseup", stopElement);
	target.style["-moz-opacity"] = ".5";
	target.style.opacity = ".5";
}

function findVar(e){
	for(var o in window){
		if(window[o]===e){
			return o;
		}
	}
	return null;
}

function Coloro() {
	// The followings can be overridden using config. e.g. coloro.config({text:"AaBbYyZz",btn:["o","x"]});
	this.text = "Aa123";
	this.btn = ["OK","Cancel"];
	this.imagePath = "images/images.png";
	this._rgb = [255, 0, 0], this._hsv = [0, 1, 1];
	this.ua = navigator.userAgent, this.isIE = /msie/i.test(this.ua) && !/opera|webtv/i.test(this.ua);
	this.pointerX = 7, this.pointerY = 262;
	this._button = 0, this._custom = 0;
	this.imageY = 0;
	this.isHexEditable;
	this.id = guid();
	
	if (!Coloro.prototype.init) {
		/**
		* http://en.wikipedia.org/wiki/HSL_color_space
		*/
		function rgb2hsv() {
			var r = this._rgb[0] / 255, g = this._rgb[1] / 255, b = this._rgb[2] / 255;
			var h, s = Math.min(r, g, b), v = Math.max(r, g, b), c = v - s;
			if (v == s) {//achromatic
				h = 0;
			} else {
				if (r == v) h = (g - b) / c;
				if (g == v) h = 2 + (b - r) / c;
				if (b == v) h = 4 + (r - g) / c;
				if (h < 0) h += 6;
				h /= 6;
			}
			if (v != 0) s = c / v;
			this._hsv = [h, s, v];
		}
		/**
		* http://en.wikipedia.org/wiki/HSL_color_space
		*/
		function hsv2rgb() {
			var h = this._hsv[0] * 6, s = this._hsv[1], v = this._hsv[2];
			var c = v * s;
			var r = 0, g = 0, b = 0;
			var x = c * (1 - Math.abs(h % 2 - 1));
			if (h < 1) r = c, g = x;
			else if (h < 2) r = x, g = c;
			else if (h < 3) g = c, b = x;
			else if (h < 4) g = x, b = c;
			else if (h < 5) r = x, b = c;
			else if (h < 6) r = c, b = x;
			if (r < 1) r += v - c;
			if (g < 1) g += v - c;
			if (b < 1) b += v - c;
			this._rgb = [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
		}
		function start(b) {
			if (b) this._button = b;
		}
		function stop() { if(this.elem && this.elem.style.display != "none") this._button = 0; }
		function custom(i) {
			this._custom = i;
			this.customi.style.borderColor = "#CCC";
			this.customi = $("coloro_custom_" + i + "_" + this.id);
			this.customi.style.borderColor = "#666";
			this.rgb(this.customi.style.backgroundColor);
		}
		function toInt(c) {
			c = c.replace("#", "").toUpperCase();
			if (c.length == 6 && !/[^0-9A-F]/g.test(c))
				return [parseInt(c.substr(0, 2), 16), parseInt(c.substr(2, 2), 16), parseInt(c.substr(4, 2), 16)];
			else if (/^RGB\(\d+, ?\d+, ?\d+\)$/.test(c))
				return c.replace(/[^0-9,]/g, "").split(",");
			else
				return null;
		}
		function toHEX() {
			return ("#" + this.pad(0) + this.pad(1) + this.pad(2)).toUpperCase();
		}
		function rgb(c) {
			if (typeof c == 'string') c = this.toInt(c);
			if (c) {
				this._rgb = c;
				this.rgb2hsv();
				this.draw();
			} else {
				return this._rgb;
			}
		}
		function hsv(c) {
			if (c) {
				this._hsv = c;
				this.hsv2rgb();
				this.draw();
			} else {
				return this._hsv;
			}
		}
		function _onComplete(f) {
			if (f) this.onComplete = f;
			else if (this.onComplete) this.onComplete(this.toHEX());
		}
		function _onColorChange(f) {
			if (f) this.onColorChange = f;
			else if (this.onColorChange) this.onColorChange(this.toHEX());
		}
		function pad(obj) {
			var str = "" + this._rgb[obj].toString(16);
			while (str.length < 2)
				str = "0" + str;
			return str;
		}
		function draw(isBox, isSlider) {
			if (this.elem){
				if (!isBox) {
					var s = this._hsv[1], v = this._hsv[2];
					this._hsv[1] = 1;
					this._hsv[2] = 1;
					this.hsv2rgb();
					this.box.style.backgroundColor = "rgb(" + this._rgb[0] + "," + this._rgb[1] + "," + this._rgb[2] + ")";
					this._hsv[1] = s, this._hsv[2] = v;
				}
				this.hsv2rgb();
				if (!isBox && !isSlider) {
					this.arrow.style.top = (this._hsv[0] * 255 + 4) + "px";
					this.pointer.style.left = (170 + this._hsv[1] * 255 + 7) + "px";
					this.pointer.style.top = ((1 - this._hsv[2]) * 255 + 7) + "px";
				}
				this.bg.style.backgroundColor = "rgb(" + this._rgb[0] + "," + this._rgb[1] + "," + this._rgb[2] + ")";
				this.txt.style.color = "rgb(" + this._rgb[0] + "," + this._rgb[1] + "," + this._rgb[2] + ")";
				this.code.value = this.toHEX();
				this.customi.style.backgroundColor = "rgb(" + this._rgb[0] + "," + this._rgb[1] + "," + this._rgb[2] + ")";
				this._onColorChange();
			}
		}
		function calc(e, b) {
			if ((this._button != 0 || b) && this.elem.style.display != "none") {
				var isBox, isSlider;
				var mxy = getMouseXY(e);
				var mx = mxy.x - parseInt(this.elem.style.left) - parseInt(this.elem.style.paddingLeft) - parseInt(this.elem.style.marginLeft);
				var my = mxy.y - parseInt(this.elem.style.top) - parseInt(this.elem.style.paddingTop) - parseInt(this.elem.style.marginTop);
				if (mx < 170) mx = 170;
				if (mx > 425) mx = 425;
				if (my < 0) my = 0;
				if (my > 255) my = 255;
				if (this._button == 1 || b == 1) { //box (x=saturation, y=value)
					isBox = true;
					this.pointer.style.left = (mx + 7) + "px";
					this.pointer.style.top = (my + 7) + "px";
				} else { //slider (hue)
					isSlider = true;
					this.arrow.style.top = (my + 4) + "px";
					this._hsv[0] = my / 256;
				}
				this._hsv[1] = (parseInt(this.pointer.style.left) - 177) / 255;
				this._hsv[2] = (255 - (parseInt(this.pointer.style.top) - 7)) / 255;
				this.draw(isBox, isSlider);
			}
			return true;
		}
		function show(e) {
			if (!e) e = window.event;
			if (!this.elem){
				var c = "coloro_";
				this.elem = $(c+this.id);
				this.pointer = $(c + "pointer_"+this.id);
				this.box = $(c + "box_"+this.id);
				this.arrow = $(c + "arrow_"+this.id);
				this.bg = $(c + "bg_"+this.id);
				this.txt = $(c + "txt_"+this.id);
				this.code = $(c + "code_"+this.id);
				this.customi = $(c + "custom_0_"+this.id);
				this.draw();
			}
			if (this.elem.style.display == "none") {
				this.elem.style.display = "block";
				var mouseXY = getMouseXY(e);
				this.elem.style.left = mouseXY.x + "px";
				this.elem.style.top = mouseXY.y + "px";
			}
		}
		function hide(e) {
			this.elem.style.display = "none";
		}
		
		function init() {
			if(!$("coloro_"+this.id)){
				var coloro = findVar(this);
				var id = this.id;
				var he = this.isHexEditable;
				//mkd(tag,id,left,top,width,height,position,cursor(move,pointer),margin,padding,border_style,border_width,
				//border_color,background_color,float,line_height,background_position,font_size,color,additions,
				//onmousedown,onclick,ondblclick,unselectable,end)
				var mkd = function(z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,q,r,s,t,v,w,x,y,aa){
					var p = "px;";
					var u = "";
					z = z == 1 ? "div" : z == 2 ? "input" : "button";
					return "<"+z+" " + 
					(a ? "id=\"coloro_" + (a == 1 ? u : a) + id + "\" " : u) +
					"style=\"" +
					(b ? "left:"+b+p : u) +
					(c ? "top:"+c+p : u) +
					(d ? "width:"+d+p : u) +
					(e ? "height:"+e+p : u) +
					(f ? "position:absolute;" : u) +
					(g ? "cursor:" + (g == 1 ? "move;" : "pointer;") : u) +
					(h ? "margin:" + (h == 1 ? "0px" : h) + ";" : u) +
					(i ? "padding:"+ (i == 1 ? "0px" : "10px") + ";" : u) +
					(j ? "border-style:solid;" : u) +
					(k ? "border-width:"+k+";" : u) +
					(l ? "border-color:"+l+";" : u) +
					(m ? "background-color:"+m+";" : u) +
					(n ? "float:left;" : u) +
					(o ? "line-height:"+o+p : u) +
					(q ? "background-position:"+q+";" : u) +
					(r ? "font-size:12px;" : u) +
					(s ? "color:"+s+";" : u) +
					(t ? t : u) +
					"\"" +
					(v ? " onmousedown=\""+ (v == 1 ? u : v) + "return false;\"" : u) +
					(w ? " onclick=\""+coloro+w+"return false;\"" : u) +
					(x ? " ondblclick=\""+coloro+x+coloro+"._onComplete();"+coloro+".hide();\"" : u) +
					(y ? " unselectable=\"on\"" : u) +
					(aa ? "></"+z+">" : u);
				};
				var dr = function(l,t,w,h){return mkd(1,0,l,t,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,"width:"+w+";height:"+h,"startDragParent(event, this.parentNode);",0,0,1,1)};
				var sh = "0px 0px 6px #999;";
				var bs = "box-shadow:";
				var html = mkd(1,1,0,0,465,0,1,0,1,2,1,"1px","#999","#FFF",0,0,0,0,0,"display:none;"+bs+sh+"-webkit-"+bs+sh+"-moz-"+bs+sh,!he,0,0,!he,0) + ">"
				+ dr("0","0","100%","10px")+dr("0","0","10px","100%")+dr("0",300,"100%","10px")+dr(475,"0","10px","100%")
				+ mkd(1,0,0,0,170,0,0,0,1,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0)+">";
				var base = [255, 0, 0];
				var pos = 2;
				var b;
				var b1;
				for (var i = 0; i < 7; i++) {
					pos--;
					if (pos < 0) pos += 3;
					b = base[pos] != 255;
					if (i == 6) base = [0, 0, 0];
					for (var j = 0; j < 5; j++) {
						b1 = base[0] + "," + base[1] + "," + base[2];
						b2 = ".rgb([" + b1 + "]);";
						html += mkd(1,0,0,0,22,22,0,2,"0px 0px " + (i == 6 ? "0px " : "14px ")+ (j == 0 ? "0px" : "10px"),1,1,"1px","#CCC","rgb(" + b1 + ")",1,0,0,0,0,0,1,b2,b2,1,1);
						i == 6 ? (base[0] += 51, base[1] += 51, base[2] += 51) : b ? base[pos] += 51 : base[pos] -= 51;
					}
				}
				
				html += "</div>"
				+ mkd(1,"pointer_",177,262,3,3,1,2,1,1,1,"2px","#999",0,0,"0",0,0,0,0,1,0,0,1,1)
				+ mkd(1,"arrow_",441,4,"0","0",1,0,0,1,1,"6px 0px 6px 6px","#F0F0F0 #F0F0F0 #F0F0F0 #333",0,0,"0",0,0,0,0,1,0,0,1,1)
				+ mkd(1,"box_",0,0,256,256,0,2,1,1,0,0,0,"#FF0000",1,0,0,0,0,0,coloro+".start(1);",".calc(event,1);",0,1,0)+">";
				var bi = (this.isIE ? "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true,sizingMethod='scale',src='" : "background-image:url('") +this.imagePath + "');";
				var bp = "px "+this.imageY+"px";
				html += mkd(1,0,0,0,256,256,0,0,1,1,0,0,0,0,0,0,"-256"+bp,0,0,bi,1,0,0,1,0)+">"
				+ mkd(1,0,0,0,256,256,0,0,1,1,0,0,0,0,0,0,"0"+bp,0,0,bi,1,0,0,1,1)+"</div></div>"
				+ mkd(1,0,0,0,24,256,0,2,"0px 0px 0px 15px",1,0,0,0,0,1,0,"-512"+bp,0,0,bi,coloro+".start(2);",".calc(event,2);",0,1,1);
				for (var i = 0; i < 5; i++) {
					html += mkd(1,"custom_"+i+"_",0,0,22,22,0,2,"10px 0px 0px "+ (i == 0 ? "0px" : "10px"),1,1,"1px",
					"#"+ (i == 0 ? "666" : "CCC"),"rgb("+(i == 0 ? "0,0,0" : "255,255,255")+");",1,0,0,0,0,0,
					1,".custom(" + i + ");",".custom(" + i + ");",1,1);
				}
				var m = "10px 0px 0px ";
				var m1 = m + "10px";
				var m2 = m + "0px";
				var btn = this.btn;
				var bu = function(x){return mkd(3,0,0,0,75,24,0,0,m2,0,0,0,0,0,1,0,0,0,0,0,1,(!x?"._onComplete();"+coloro:"")+".hide();",0,1,0)+">"+btn[x]+"</button>";};
				html += mkd(1,"bg_",0,0,22,22,0,0,m1,1,1,"1px","#333","#000",1,0,0,0,0,0,1,0,0,1,1)
				+ mkd(1,"txt_",0,0,50,24,0,0,m2,1,0,0,0,"#FFF",1,24,0,1,"#000","text-align:center;",!he,0,0,!he,0)+">"+this.text+"</div>"
				+ mkd(2,"code_",0,0,58,22,0,0,m1,1,1,"1px","#CCC",0,1,24,0,1,"#333",0,0,0,0,0,0) + "value=\"#000000\" maxLength=\"7\" onkeyup=\""+coloro+".rgb(this.value);\" />"
				+"<div>"
				+ (btn[0] ? bu(0) : "")
				+ (btn[1] ? bu(1) : "")
				+"</div></div>";
				var div = doc.createElement("div"); //not ideal but only 1ms gain in IE6... so...
				div.innerHTML = html;
				doc.body.appendChild(div.firstChild);
				addEL(doc, "mousemove", bind(this, this.calc));
				addEL(doc, "mouseup", bind(this, this.stop));
			}
		}
		function config(obj){
			for(var o in obj)
				this[o] = obj[o];
		}
		var obj = {
			init:init,
			show:show,
			hide:hide,
			calc:calc,
			draw:draw,
			rgb2hsv:rgb2hsv,
			hsv2rgb:hsv2rgb,
			toInt:toInt,
			toHEX:toHEX,
			pad:pad,
			rgb:rgb,
			hsv:hsv,
			start:start,
			stop:stop,
			_onComplete:_onComplete,
			_onColorChange:_onColorChange,
			custom:custom,
			config:config
		};
		for(var o in obj)
			Coloro.prototype[o] = obj[o];
	}
	addEL(window, "load", bind(this, this.init));
}